iT邦幫忙

2025 iThome 鐵人賽

DAY 16
0

「透過通路合作,讓防災物資能夠更輕易的購入,提升防災物資準備率。」

在專案的前半段,我們已經討論過前端設計、後端API、資料庫模型與AI推薦邏輯。
下一個現實問題是:

這些物資,使用者要去哪裡買?
若是本專案最後僅以「一份靜態防災物資建議清單」作收,則稍嫌可惜。
為使用者建立一段更容易採購的路徑,方能夠如 「韌性生活指南」 Day1所說: 「日常有備,災來無懼」

這也是,今天要探討的一個關鍵環節: 設計一個可行的電商合作策略


為什麼要串接電商?

  • 降低門檻: 點擊即可採購,不用再去搜尋
  • 提升轉換: 專案不只是提醒,而是直接引導行動
  • 擴充性: 未來能延伸到合作專區、甚至 API 串接

以策略層面來說,我把電商合作分為三個層次:
1.短期(Demo/原型製作階段)

  • 導購連結:以低門檻方式,將物資與電商商品頁連結起來。
  • 免責彈窗:提醒使用者「即將跳轉到第三方網站,本平台不負責售後」。
  • 重點:快速展示「行動化」的可行性,證明專案能幫助使用者立刻採取行動。
    2.中期(專案驗證階段)
  • 專屬選品專區:與電商洽談,由平台開設「韌性生活指南」專區,集中呈現推薦物資。
  • 好處:維護成本降低,品牌調性提升,用戶體驗更直觀。
    3.長期(商業化階段)
  • API串接:若電商願意開放 API,平台可以直接抓取即時庫存、價格與商品資訊。
  • 最佳體驗:使用者在本平台即可完成搜尋、比價,甚至下單。

第一版方案核心:「選品卡片+免責彈窗+導購連結」

在技術設計層面,我提出了第一個落地方案:

  1. 選品卡片: 顯示物資名稱、需求量與情境說明
  2. 電商連結: 每個物資下方放上不同電商的 ICON 按鈕
  3. 免責彈窗: 提醒使用者「即將離開本站,前往第三方平台,本平台不負責售後」
  4. 外連導購: 同意後才跳轉到對應的電商商品頁或搜尋結果

好處:技術門檻低、能快速上線/體驗、維持版面的整潔、專注在資訊傳達的正確性。
限制:需要人工維護連結,無法自動更新商品庫存或價格。

做法總覽(無API版)

  1. 選品資料表: 為每個物資(如「水」)準備一筆設定,內含情境說明與各電商的「目標連結」。
    • 連結可用兩種:
      • 精選商品頁(手動挑的最適合商品)
      • 站內搜尋結果(以關鍵字/容量/品牌預先組好的結果頁)
  2. 前端卡片 UI: 顯示插圖、標題、需求數量與情境說明,下方放各電商的ICON按鈕
  3. 導購細節: 外連新分頁開啟(target="_blank" + rel="noopener noreferrer"),並建議加上 UTM追蹤參數

可直接使用的最小範例(HTML+CSS+JS)

貼到一個 .html 檔即可跑;把 LINKS 裡的網址換成你實際整理的頁面或站內搜尋結果。

<!doctype html>
<html lang="zh-Hant">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>韌性生活指南|物資卡片示例</title>
<style>
  body { font-family: system-ui, "Noto Sans TC", sans-serif; background:#f7f7f8; padding:24px; color:#111;}
  .card { background:#fff; border-radius:16px; padding:20px; max-width:820px; margin:auto; box-shadow:0 6px 24px rgba(0,0,0,.06);}
  .row { display:flex; gap:16px; align-items:flex-start; flex-wrap:wrap;}
  .thumb { width:96px; height:96px; border-radius:12px; background:#eef2f7 url('') center/60% no-repeat; flex:0 0 96px; }
  h2 { margin:0 0 4px; font-size:22px;}
  .meta { color:#666; font-size:14px; margin:4px 0;}
  .desc { margin:12px 0 20px; color:#333; line-height:1.6;}
  .divider { border-top:1px solid #eee; margin:16px 0;}
  .shops { display:flex; gap:12px; flex-wrap:wrap;}
  .shop-btn { display:flex; align-items:center; justify-content:center; gap:8px;
              padding:12px 16px; border:1px solid #e5e7eb; border-radius:999px; background:#fff;
              cursor:pointer; transition:.2s; min-width:160px; }
  .shop-btn:hover { transform: translateY(-1px); box-shadow:0 6px 16px rgba(0,0,0,.06);}
  .shop-logo { height:20px; }
  /* Modal */
  .modal-backdrop{ position:fixed; inset:0; background:rgba(17,24,39,.55); display:none; align-items:center; justify-content:center; z-index:40;}
  .modal{ width:min(520px, 92vw); background:#fff; border-radius:16px; padding:20px 20px 16px; box-shadow:0 20px 60px rgba(0,0,0,.2);}
  .modal h3{ margin:0 0 8px; font-size:18px;}
  .modal ul{ margin:8px 0 16px 18px; color:#444; }
  .modal .danger{ color:#b91c1c; font-weight:700;}
  .actions{ display:flex; gap:10px; justify-content:flex-end; }
  .btn{ padding:10px 14px; border-radius:10px; border:1px solid #e5e7eb; cursor:pointer; background:#fff;}
  .btn.primary{ background:#111827; color:#fff; border-color:#111827;}
</style>
</head>
<body>

<div class="card" id="item-card">
  <div class="row">
    <div class="thumb" style="background-image:url('https://img.icons8.com/ios-filled/100/water.png'); background-size:54%"></div>
    <div style="flex:1">
      <div class="meta">分類:食品</div>
      <h2>用水</h2>
      <div class="meta"><strong>所需數量:</strong><span id="need-text">17 公升</span></div>
      <p class="desc">一個人維持生命所需的水量會因年齡和體重而不同,但建議每人每天的飲用水量為 3 公升。</p>
    </div>
  </div>

  <div class="divider"></div>

  <div>
    <div class="meta" style="margin-bottom:8px;">線上購買</div>
    <div class="shops" id="shop-buttons"></div>
  </div>
</div>

<!-- 免責彈窗 -->
<div class="modal-backdrop" id="modal">
  <div class="modal" role="dialog" aria-modal="true">
    <h3>筆記</h3>
    <ul>
      <li><span class="danger">從現在起,該網站不再由本平台管理或經營。</span></li>
      <li>本平台對於於第三方網站購買之產品所造成的任何損害或問題不承擔任何責任。</li>
      <li>請小心分辨第三方網站與其產品資訊。</li>
    </ul>
    <div class="actions">
      <button class="btn" id="btn-cancel">取消</button>
      <button class="btn primary" id="btn-continue">同意並進入下一頁</button>
    </div>
  </div>
</div>

<script>
  // 1) 物資選品設定(無 API 版)
  //    你可以為每個物資定義不同平台的目標連結(精選商品或站內搜尋)
  const LINKS = {
    water: {
      momo: {
        label: "momo",
        // 例:精選商品或搜尋結果(建議加上 UTM 以追蹤來源)
        url: "https://www.momoshop.com.tw/search/searchShop.jsp?keyword=%E7%93%B6%E8%A3%9D%E6%B0%B4&utm_source=rensheng&utm_medium=referral&utm_campaign=water",
        logo: "https://seeklogo.com/images/M/momo-logo-6F1F5D7C2B-seeklogo.com.png"
      },
      pchome: {
        label: "PChome 24h",
        url: "https://24h.pchome.com.tw/store/DSAJ2L?utm_source=rensheng&utm_medium=referral&utm_campaign=water",
        logo: "https://upload.wikimedia.org/wikipedia/commons/5/5f/PChome24h.png"
      },
      eslite: {
        label: "誠品線上",
        // 若誠品未有對應商品,可改成站內搜尋或你們談好的專區
        url: "https://www.eslite.com/Search?keyword=%E6%B0%B4%20%E6%8A%97%E7%81%BD&utm_source=rensheng&utm_medium=referral&utm_campaign=water",
        logo: "https://upload.wikimedia.org/wikipedia/commons/3/35/Eslite_logo.svg"
      }
    }
  };

  // 2) 建立按鈕(用 water 作為示範)
  const shopsEl = document.getElementById('shop-buttons');
  const platforms = LINKS.water;
  let pendingUrl = null;

  Object.keys(platforms).forEach(key => {
    const { label, url, logo } = platforms[key];
    const btn = document.createElement('button');
    btn.className = 'shop-btn';
    btn.innerHTML = `<img class="shop-logo" src="${logo}" alt="${label}" /> <span>${label}</span>`;
    btn.addEventListener('click', () => openDisclaimer(url));
    shopsEl.appendChild(btn);
  });

  // 3) 免責流程:先顯示彈窗,按「同意」才外連
  const modal = document.getElementById('modal');
  const btnCancel = document.getElementById('btn-cancel');
  const btnContinue = document.getElementById('btn-continue');

  function openDisclaimer(url) {
    pendingUrl = url;
    modal.style.display = 'flex';
  }
  btnCancel.onclick = () => { modal.style.display = 'none'; pendingUrl = null; };
  btnContinue.onclick = () => {
    if (pendingUrl) {
      // 新分頁開啟 + 安全屬性
      window.open(pendingUrl, '_blank', 'noopener,noreferrer');
    }
    modal.style.display = 'none';
    pendingUrl = null;
  };

  // 4) 依你的人數/天數動態換算所需量(此處僅示例)
  //    例如:每人每天 3 公升、備 5 天;顯示在 #need-text
  const people = 1, days = 6, perDayLiters = 3;
  document.getElementById('need-text').textContent = `${people * days * perDayLiters} 公升`;
</script>
</body>
</html>

需修改之處有三:

1.LINKS 物件:把各平台的連結換成我整理好的「商品頁」或「站內搜尋」網址(記得加 UTM)。
2.logo圖檔:可換成我本地或CDN的品牌ICON。
3.「所需數量」換算:把示範公式替換成我實際的邏輯。


維運SOP(無API版)

  • 每週檢查一次: 連結是否下架/失效;若失效改用該平台的搜尋結果頁。
  • 分眾關鍵字: 針對「水」可用多組關鍵字:礦泉水 600ml 整箱保久水淨水濾芯
  • 追蹤表: 為每個物資 × 平台建立表格,含「連結、最後更新日、價格區間、是否缺貨」。
  • UTM 統計: 在 GA/Matomo 觀察各平台點擊與轉換,淘汰成效差的連結。

進階(可選)

  • 專屬選品頁: 與「本土大型電商」/「文化選品型電商」/「日常生活零售平台」談合作,做「韌性生活指南」專區;把 LINKS 改成該專區 URL,一次呈現所有物資。
  • 多地區支援: 針對台灣/日本/海外分別放對應平台(例:台灣 = momo/PChome,日 = Amazon JP/Rakuten)。
  • 合規提醒: 彈窗文案放在共用模組,確保所有外連一致顯示。

web component(網頁元件)

核心概念

  • 好處1: 將前述導購方案模組化、可重複使用、維護成本低。
  • 好處2: 不同物資只要換 JSON,就能快速生成新的卡片。
  • 用一個自訂元件 <resilience-item> 來承載「物資卡片」
  • 我只需要提供 一份 JSON (包含物資名稱、需求數量、情境說明、各電商連結(含 logo 與 URL))
  • 自動生成完整的卡片UI、免責彈窗與導購按鈕

更便於管理導購連結,降低因為人工維護過於繁瑣而卡關。

<!doctype html>
<html lang="zh-Hant">
<head>
<meta charset="utf-8"/>
<title>韌性生活指南|Web Component Demo</title>
<script>
class ResilienceItem extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: "open" });
  }

  connectedCallback() {
    const data = JSON.parse(this.getAttribute("data"));
    this.render(data);
  }

  render(data) {
    const style = `
      <style>
        .card { background:#fff; border-radius:16px; padding:20px; box-shadow:0 6px 24px rgba(0,0,0,.06); max-width:780px; }
        h2 { margin:0 0 4px; font-size:20px;}
        .meta{color:#666; font-size:14px;}
        .desc{margin:10px 0 16px; line-height:1.6;}
        .shops{display:flex; gap:12px; flex-wrap:wrap;}
        .shop-btn{border:1px solid #e5e7eb; border-radius:999px; padding:10px 14px; background:#fff; cursor:pointer; transition:.2s; display:flex; align-items:center; gap:6px;}
        .shop-btn:hover{box-shadow:0 4px 12px rgba(0,0,0,.1);}
        .modal-backdrop{position:fixed; inset:0; background:rgba(0,0,0,.55); display:none; align-items:center; justify-content:center; z-index:999;}
        .modal{background:#fff; padding:20px; border-radius:12px; max-width:520px;}
        .actions{display:flex; gap:10px; justify-content:flex-end; margin-top:12px;}
        .btn{padding:8px 14px; border-radius:8px; border:1px solid #ddd; cursor:pointer;}
        .btn.primary{background:#111; color:#fff;}
      </style>`;

    const shopsHTML = data.shops.map(s => `
      <button class="shop-btn" data-url="${s.url}">
        <img src="${s.logo}" alt="${s.name}" height="18"/> ${s.name}
      </button>`).join("");

    const template = `
      <div class="card">
        <div class="meta">分類:${data.category}</div>
        <h2>${data.name}</h2>
        <div class="meta"><strong>所需數量:</strong>${data.need}</div>
        <p class="desc">${data.desc}</p>
        <div class="shops">${shopsHTML}</div>
      </div>

      <div class="modal-backdrop" id="modal">
        <div class="modal">
          <h3>筆記</h3>
          <ul>
            <li><span style="color:#b91c1c;font-weight:700">從現在起,該網站不再由本平台管理或經營。</span></li>
            <li>本平台對於於第三方網站購買之產品所造成的任何損害或問題不承擔任何責任。</li>
            <li>請小心分辨第三方網站與其產品資訊。</li>
          </ul>
          <div class="actions">
            <button class="btn" id="btn-cancel">取消</button>
            <button class="btn primary" id="btn-continue">同意並進入下一頁</button>
          </div>
        </div>
      </div>
    `;

    this.shadowRoot.innerHTML = style + template;

    // modal 邏輯
    const modal = this.shadowRoot.querySelector("#modal");
    const btnCancel = this.shadowRoot.querySelector("#btn-cancel");
    const btnContinue = this.shadowRoot.querySelector("#btn-continue");
    let pendingUrl = null;

    this.shadowRoot.querySelectorAll(".shop-btn").forEach(btn => {
      btn.addEventListener("click", () => {
        pendingUrl = btn.getAttribute("data-url");
        modal.style.display = "flex";
      });
    });
    btnCancel.onclick = () => { modal.style.display = "none"; pendingUrl = null; };
    btnContinue.onclick = () => {
      if (pendingUrl) window.open(pendingUrl, "_blank", "noopener,noreferrer");
      modal.style.display = "none"; pendingUrl = null;
    };
  }
}

customElements.define("resilience-item", ResilienceItem);
</script>
</head>
<body style="font-family:sans-serif;background:#f7f7f8;padding:20px">

<resilience-item data='{
  "category":"食品",
  "name":"用水",
  "need":"17 公升",
  "desc":"建議每人每天的飲用水量為 3 公升。",
  "shops":[
    {"name":"momo","url":"https://www.momoshop.com.tw/search/searchShop.jsp?keyword=%E7%93%B6%E8%A3%9D%E6%B0%B4","logo":"https://seeklogo.com/images/M/momo-logo-6F1F5D7C2B-seeklogo.com.png"},
    {"name":"PChome","url":"https://24h.pchome.com.tw/store/DSAJ2L","logo":"https://upload.wikimedia.org/wikipedia/commons/5/5f/PChome24h.png"},
    {"name":"誠品線上","url":"https://www.eslite.com/Search?keyword=%E6%B0%B4","logo":"https://upload.wikimedia.org/wikipedia/commons/3/35/Eslite_logo.svg"}
  ]
}'></resilience-item>

</body>
</html>

發展藍圖

Demo/原型製作階段 vs 商業化階段

  • Demo 階段 (現在):用導購連結(低門檻)與 Web Component 展示原型,讓使用者看見專案的可行性。
  • 商業化階段 (未來):
    • 中期談「選品專區」合作(高整合度),讓用戶能在電商官方頁面一次採購。
    • 長期爭取 API 串接,真正實現「智慧化、自動化、個人化」的物資推薦與採購體驗。(即時庫存、價格更新、直接下單)

階段性目標:先證明現在的「可行性」,爭取未來的「可擴充性」。


結語

電商合作不是附加功能,更是讓「韌性生活指南」真正走進日常生活的關鍵。
它讓平台不僅能給予提醒,更能化為具體行動, 縮短從「意識」到「實踐」的距離
因為準備防災物資,從來不只是一個清單的問題,而是一連串「行動」的組成。

這是我在構思「Day 16|導購方式:如何簡化防災物資採購門檻?」時,回想起發想此專案的初衷,嘗試建立的導購策略和通路合作發展方向:如何讓「韌性生活指南」透過電商合作,走得更穩、更遠。


上一篇
Day 15|AI 模組:推薦邏輯與情境化解釋
系列文
《韌性生活指南:用科技打造更堅韌的日常》16
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言